package cool.taomu.toolkit.service.kademlia.service

import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.google.common.collect.Lists
import com.google.inject.Inject
import com.google.inject.name.Named
import cool.taomu.toolkit.service.kademlia.entity.KPeerNode
import java.math.BigInteger
import java.util.ArrayList
import java.util.List
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit
import org.apache.commons.beanutils.BeanUtils
import org.eclipse.xtend.lib.annotations.Accessors
import org.slf4j.LoggerFactory

@Accessors
class KRoutingTable {
    val static LOG = LoggerFactory.getLogger(KRoutingTable);

    @Inject
    @Named("taomu.toolkit.kademlia.local.node")
    var KPeerNode localNode; // 当前节点
    @Inject
    @Named("taomu.toolkit.kademlia.kbuckets")
    var List<ArrayBlockingQueue<KPeerNode>> buckets; // 路由表桶
    val Cache<String, BigInteger> cache

    var int bucketsTotalSize = 0;

    @Inject(optional=true)
    @Named("taomu.kademlia.k")
    int k = 8;

    new() {
        this.cache = CacheBuilder.newBuilder.maximumSize(160 * k) // 最多缓存 1000 个条目
        .expireAfterWrite(10, TimeUnit.MINUTES) // 写入后 10 分钟过期
        .build
    }

    def static nodesFindByHostAndPort(ArrayBlockingQueue<KPeerNode> nodes, KPeerNode node) {
        return nodes.findFirst[it.host == node.host && it.port == node.port]
    }

    /**
     * 添加节点到路由表
     */
    def synchronized boolean addPeerNode(KPeerNode node, ICheckPeer checkPeer) {
        // 判断加入的节点不是本地节点
        if (node.host.equalsIgnoreCase("localhost") || node.equals(this.localNode) ||
            (node.host.equals(this.localNode.host) && node.port == this.localNode.port) || node.id.trim.equals("")) {
            return false;
        }
        LOG.info("本地节点 {} \n尝试将节点 {} 添加到K桶中", this.localNode, node)
        // 获取节点所属的桶索引
        var int bucketIndex = getBucketIndex(localNode.getId(), node.getId());
        // 获取桶索引对应的队列
        val queue = buckets.get(bucketIndex);
        // 根据node中的host和port查找队列中是否存在这个节点
        val bucketsNode = queue.nodesFindByHostAndPort(node);
        // 如果判断bucketsNode.id != node.id
        if (bucketsNode !== null && !bucketsNode.id.equals(node.id)) {
            LOG.info("更新节点id {}\n==>\n{}", bucketsNode, node);
            bucketsNode.id = node.id;
        }
        // 为 null 是不存在这个节点
        if (bucketsNode === null) {
            if (queue.size < this.k) {
                LOG.info("节点{}不存在K桶中，将其加入k桶", node);
                if (queue.add(node)) {
                    LOG.info("加入k桶成功");
                }
                LOG.info("k桶大小:{}", queue.size);
            } else {
                val knode = queue.take();
                LOG.info("对节点{}进行ping判断", node);
                try {
                    if (checkPeer !== null && checkPeer.apply(knode)) {
                        LOG.info("节点{}成功返回PONG,将其加入k桶队列末尾", knode);
                        queue.add(knode);
                    } else {
                        LOG.info("将新节点{}加入k桶成功", node);
                        queue.add(node);
                    }
                } catch (Exception ex) {
                    LOG.error(ex.message);
                    if (queue.add(node)) {
                        LOG.info("将新节点{}加入k桶成功", node);
                    } else {
                        LOG.info("将新节点{}加入k桶失败", node);
                    }
                }
            }
        } else {
            LOG.info("节点{}存在K桶中，将其移动到末尾", node);
            var temp = queue.toList;
            temp.remove(bucketsNode);
            queue.clear;
            queue.addAll(temp);
            if (bucketsNode.id.equals(node.id)) {
                LOG.info("k桶中的节点id相同");
                queue.add(bucketsNode);
                return false;
            } else if (checkPeer !== null && checkPeer.apply(node)) {
                LOG.info("k桶中的节点id不相同,添加新节点");
                queue.add(node);
            } else {
                LOG.info("将新节点{}加入k桶失败", node);
            }
        }
        this.bucketsTotalSize = buckets.stream().mapToInt[it.size].sum();
        LOG.info("bucketsTotalSize:{}", bucketsTotalSize);
        return true;
    }

    def ksize() {
        if (buckets !== null) {
            this.bucketsTotalSize = buckets.stream().mapToInt[it.size].sum();
            return this.bucketsTotalSize
        }
        return -1;
    }

    def List<KPeerNode> findClosestNodes(String targetId) {
        findClosestNodes(targetId, this.k)
    }

    // 查找距离目标ID最近的k个节点
    def List<KPeerNode> findClosestNodes(String targetId, int k) {
        LOG.info("buckets total size:{},查找距离目标ID:{} 最近的{}个节点", this.bucketsTotalSize, targetId, k);
        var closestNodes = new ArrayList<KPeerNode>();
        for (var int i = 0; i < buckets.size && closestNodes.size < k; i++) {
            if (!buckets.get(i).empty) {
                LOG.info("找到节点");
                closestNodes.addAll(buckets.get(i));
            }
        }
        LOG.info("找到节点数据:{}", closestNodes.size);
        closestNodes.sort([ n1, n2 |
            xorDistance(targetId, n1.getId()).compareTo(xorDistance(targetId, n2.getId()))
        ]);
        return closestNodes.subList(0, Math.min(k, closestNodes.size()));
    }

    // 获取节点所属的桶索引
    def int getBucketIndex(String id1, String id2) {
        return xorDistance(id1, id2).bitCount;
    }

    // 带缓存的异或距离方法
    def BigInteger xorDistance(String id1, String id2) {
        val key = key(id1, id2)
        return cache.get(key.join("#$#"))[computeXor(id1, id2)]
    }

    // 构建 key 并排序，保证 (a,b) 和 (b,a) 是同一个 key
    private def List<String> key(String id1, String id2) {
        LOG.info("key id1 {} id2 {}", id1, id2);
        if (id1.compareTo(id2 === null ? "" : id2) <= 0) {
            return Lists.newArrayList(id1, id2)
        } else {
            return Lists.newArrayList(id2, id1)
        }
    }

    // 实际计算逻辑（只有缓存未命中时才执行）
    private def BigInteger computeXor(String id1, String id2) {
        val bigInt1 = new BigInteger(id1, 2)
        val bigInt2 = new BigInteger(id2, 2)
        return bigInt1.xor(bigInt2)
    }
}
